/*
 * (C) Copyright 2006
 * Texas Instruments, <www.ti.com>
 * Jian Zhang <jzhang@ti.com>
 * Richard Woodruff <r-woodruff2@ti.com>
 * 
 * Modified for Stalker board
 * Jason Lam <lzg@ema-tech.com>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include <common.h>
#include <command.h>
#include <part.h>
#include <fat.h>
#include <i2c.h>
#include <asm/arch/cpu.h>
#include <asm/arch/bits.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch/sys_info.h>
#include <asm/arch/clocks.h>
#include <asm/arch/mem.h>

#define __raw_readl(a)		(*(volatile unsigned int *)(a))
#define __raw_writel(v, a)	(*(volatile unsigned int *)(a) = (v))
#define __raw_readw(a)		(*(volatile unsigned short *)(a))
#define __raw_writew(v, a)	(*(volatile unsigned short *)(a) = (v))

/*******************************************************
 * Routine: delay
 * Description: spinning delay to use before udelay works
 ******************************************************/
static inline void delay(unsigned long loops)
{
	__asm__ volatile ("1:\n" "subs %0, %1, #1\n"
			  "bne 1b":"=r" (loops):"0"(loops));
}

void udelay (unsigned long usecs) {
	delay(usecs);
}

/*********************************************************************
 * wait_on_value() - common routine to allow waiting for changes in
 *   volatile regs.
 *********************************************************************/
u32 wait_on_value(u32 read_bit_mask, u32 match_value, u32 read_addr, u32 bound)
{
	u32 i = 0, val;
	do {
		++i;
		val = __raw_readl(read_addr) & read_bit_mask;
		if (val == match_value)
			return 1;
		if (i == bound)
			return 0;
	} while (1);
}

/*
 * OMAP3630 specific functions
 */
static void dpll3_init_36xx(u32 sil_index, u32 clk_index)
{

	__raw_writel(6, 	CM_CLKEN_PLL);
//	wait_on_value(BIT0, 0, CM_IDLEST_CKGEN, LDELAY);
	__raw_writel(0x8c80c00, CM_CLKSEL1_PLL);
	__raw_writel(0x322,     CM_CLKSEL_CORE);
	__raw_writel(0x7,      CM_CLKEN_PLL);
//	wait_on_value(BIT0, 1, CM_IDLEST_CKGEN, LDELAY);
}

static void mpu_init_36xx(u32 sil_index, u32 clk_index)
{
	__raw_writel(0x1,        CM_CLKSEL2_PLL_MPU);
	__raw_writel(0xc80c,     CM_CLKSEL1_PLL_MPU);
	__raw_writel(0x7,     CM_CLKEN_PLL_MPU);
//	wait_on_value(BIT0, 1, CM_IDLEST_PLL_MPU, LDELAY);
}
/******************************************************************************
 * prcm_init() - inits clocks for PRCM as defined in clocks.h
 *   -- called from SRAM, or Flash (using temp SRAM stack).
 *****************************************************************************/
void prcm_init(void)
{
	__raw_writel(3, PRM_CLKSEL);
	dpll3_init_36xx(0, 3);
	mpu_init_36xx(0, 3);
	__raw_writel(0xff, CM_CLKSEL_PER);
	__raw_writel(0x1,  CM_CLKSEL_WKUP);
	delay(5000);
}
/**********************************************************
 * Routine: s_init
 * Description: Does early system init of muxing and clocks.
 * - Called at time when only stack is available.
 **********************************************************/

void s_init(void)
{
	__raw_writel(0x4, CM_ICLKEN_WKUP);
	__raw_writel(FCK_CORE1_ON, CM_FCLKEN1_CORE);
	__raw_writel(ICK_CORE1_ON, CM_ICLKEN1_CORE);
	__raw_writel(ICK_CORE2_ON, CM_ICLKEN2_CORE);
	__raw_writel(FCK_WKUP_ON, CM_FCLKEN_WKUP);
	__raw_writel(ICK_WKUP_ON, CM_ICLKEN_WKUP);
	__raw_writel(FCK_PER_ON, CM_FCLKEN_PER);
	__raw_writel(ICK_PER_ON, CM_FCLKEN_PER);
	delay(1000);

	prcm_init();

	/***************** SDRAM *******************************/
	/* reset sdrc controller */
	__raw_writel(SOFTRESET, SDRC_SYSCONFIG);
	wait_on_value(BIT0, BIT0, SDRC_STATUS, 12000000);
	__raw_writel(0, SDRC_SYSCONFIG);

	/* setup sdrc to ball mux */
	__raw_writel(SDP_SDRC_SHARING, SDRC_SHARING);

		__raw_writel(0x2, SDRC_CS_CFG); /* 256MB/bank */
		__raw_writel(SDP_SDRC_MDCFG_0_DDR_HYNIX, SDRC_MCFG_0);
		__raw_writel(SDP_SDRC_MDCFG_0_DDR_HYNIX, SDRC_MCFG_1);
		__raw_writel(HYNIX_V_ACTIMA_200, SDRC_ACTIM_CTRLA_0);
		__raw_writel(HYNIX_V_ACTIMB_200, SDRC_ACTIM_CTRLB_0);
		__raw_writel(HYNIX_V_ACTIMA_200, SDRC_ACTIM_CTRLA_1);
		__raw_writel(HYNIX_V_ACTIMB_200, SDRC_ACTIM_CTRLB_1);
		__raw_writel(SDP_3430_SDRC_RFR_CTRL_200MHz, SDRC_RFR_CTRL_0);
		__raw_writel(SDP_3430_SDRC_RFR_CTRL_200MHz, SDRC_RFR_CTRL_1);

	__raw_writel(SDP_SDRC_POWER_POP, SDRC_POWER);

	/* init sequence for mDDR/mSDR using manual commands (DDR is different) */
	__raw_writel(CMD_NOP, SDRC_MANUAL_0);
	__raw_writel(CMD_NOP, SDRC_MANUAL_1);

	delay(5000);

	__raw_writel(CMD_PRECHARGE, SDRC_MANUAL_0);
	__raw_writel(CMD_PRECHARGE, SDRC_MANUAL_1);

	__raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_0);
	__raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_1);

	__raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_0);
	__raw_writel(CMD_AUTOREFRESH, SDRC_MANUAL_1);

	/* set mr0 */
	__raw_writel(SDP_SDRC_MR_0_DDR, SDRC_MR_0);
	__raw_writel(SDP_SDRC_MR_0_DDR, SDRC_MR_1);

	/* set up dll */
	__raw_writel(SDP_SDRC_DLLAB_CTRL, SDRC_DLLA_CTRL);
	delay(0x2000);	/* give time to lock */
}

int nand_init(void)
{
	/* global settings */
	__raw_writel(0x10, GPMC_SYSCONFIG);	/* smart idle */
	__raw_writel(0x0, GPMC_IRQENABLE);	/* isr's sources masked */
	__raw_writel(0, GPMC_TIMEOUT_CONTROL);/* timeout disable */

	/* Set the GPMC Vals, NAND is mapped at CS0, oneNAND at CS0.
	 *  We configure only GPMC CS0 with required values. Configiring other devices
	 *  at other CS is done in u-boot. So we don't have to bother doing it here.
	 */
	__raw_writel(0 , GPMC_CONFIG7 + GPMC_CONFIG_CS0);
	delay(1000);

		__raw_writel(M_NAND_GPMC_CONFIG1, GPMC_CONFIG1 + GPMC_CONFIG_CS0);
		__raw_writel(M_NAND_GPMC_CONFIG2, GPMC_CONFIG2 + GPMC_CONFIG_CS0);
		__raw_writel(M_NAND_GPMC_CONFIG3, GPMC_CONFIG3 + GPMC_CONFIG_CS0);
		__raw_writel(M_NAND_GPMC_CONFIG4, GPMC_CONFIG4 + GPMC_CONFIG_CS0);
		__raw_writel(M_NAND_GPMC_CONFIG5, GPMC_CONFIG5 + GPMC_CONFIG_CS0);
		__raw_writel(M_NAND_GPMC_CONFIG6, GPMC_CONFIG6 + GPMC_CONFIG_CS0);

		/* Enable the GPMC Mapping */
		__raw_writel((((OMAP34XX_GPMC_CS0_SIZE & 0xF)<<8) |
			     ((NAND_BASE_ADR>>24) & 0x3F) |
			     (1<<6)),  (GPMC_CONFIG7 + GPMC_CONFIG_CS0));
		delay(2000);

		if (nand_chip()) 
			return 1;

	return 0;
}
